<?php
namespace App\Listener;

use App\Rpc\Lib\MessageInterface;
use App\Rpc\Lib\OrderInterface;
use Six\Rpc\Client\Annotation\Mapping\Reference;
use Six\Rpc\Client\ServiceContext;
use Swoft\Context\Context;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Redis\Pool;
use Swoft\Server\Swoole\SwooleEvent;
/**
 * Class MessageStatusListener
 * @package App\Listener
 * @Listener(SwooleEvent::START)
 */
class MessageStatusListener implements EventHandlerInterface
{
    /**
     * 在程序初始化的时候定义好服务降级处理类
     * @Reference(pool="order.pool" , fallback="OrderFallback")
     *
     * @var OrderInterface
     */
    private $orderService;

    /**
     * 消息确认并投递
     * @Reference(pool="message.pool" , fallback="MessageFallback")
     *
     * @var MessageInterface
     */
    private $messageService;

    public function handle(EventInterface $event): void
    {

        //多进程实现
        $time = 5; #超时任务
        \Swoole\Timer::tick(2000,function () use ($time){
            sgo(function () use ($time){
                //自动初始化一个Context上下文对象(协程环境下)
                $context = ServiceContext::new();
                Context::set($context);

                try{
                    $connectionRedis = bean(Pool::DEFAULT_POOL)->getConnection();
                    $connectionRedis->setRelease(true);#可以重用

                    //查询超时未确认的任务
                    $result = $connectionRedis->zRangeByScore('message_system_time','-inf',(string)(time()-$time));

                    #判断业务的状态是预发送，并且是确认消息状态，如果主动方任务成功，就投递，否怎删除消息
                    foreach ($result as $item=> $msg_id){
                        $data = $connectionRedis->hget('message_system',(string)$msg_id);
                        if(!empty($data)){
//                            var_dump('查询超时未确认的任务',$data);
                            $data = json_decode($data , true);
                            if($data['status'] == 2){
                                #状态为2代表的是已投递，超时没有被正确消费（消息恢复子系统来处理）
                                //重试重新投递
//                                var_dump($data);
                                if($data['message_retries_number'] >= 2){
                                    var_dump($data['message_retries_number'],'消息重新投递中...');
                                    $connectionRedis->transaction(function (\Redis $redis) use ($msg_id,$data){
                                        $redis->hDel("message_system",(string)$msg_id);
                                        $redis->zRem('message_system_time',(string)$msg_id);
                                        //2、投递次数超过最大值，删除任务，并且存储到单独的redis一个队列当中
                                        //消息已死亡，放在某个队列当中，在消息管理系统中手动恢复
                                        $data['status'] = 4;
                                        $redis->lPush('message_system_dead',json_encode($data));
                                    });
                                }
                                $this->messageService->confirmMsgToSend($msg_id,2); //重新投递业务
//
                            }elseif ($data['status'] == 1){ //消息状态子系统（已经进入消息子系统，但是未确认）
                                #主动方任务是成功的，更改状态，第一次投递到被动方的服务

                                //1、查询任务结果（主动方任务是成功的，更改状态，第一次投递到被动方服务）
                                $stateJob = $this->orderService->confirmStatus($msg_id);
//                                var_dump($stateJob);
                                if($stateJob['status'] == 1){
                                    //确认并投递
                                    $result = $this->messageService->confirmMsgToSend($msg_id,1); //确认并投递消息
//                                    var_dump('消息已存储未确认',$result);
                                }elseif ($stateJob['status'] == 0){
                                    ////3、订单状态更新任务失败（删除任务），则移除消息
                                    $this->messageService->ackMsg($msg_id);
                                }


                            }
                        }
                    }

                    #确认业务状态：业务成功则投递，业务失败则删除


                    $connectionRedis->release();
                }catch (\Exception $exception){
                    var_dump($exception->getMessage());
                }
            });
        });


    }
}
























